<?php
/**
 * Created by PhpStorm.
 * User: Artem
 * Date: 04.12.2014
 * Time: 22:19
 */

namespace Orm;


abstract class AbstractRepo
{

    abstract protected function getTableName();

    abstract protected function getMapping(); //перечисляются имена полей

    abstract protected function getInstance(); //получить чистую копию

    private function getFieldCanonicalName($string)
    {
        while (strpos($string, '_') > 0) {
            $pos = strpos($string, '_');
            $string = substr_replace($string, '', $pos, 1);
            $letter = strtoupper(substr($string, $pos, 1));
            $string = substr_replace($string, $letter, $pos, 1);
        }
        return $string;
    }

    /**
     * @param object $arr
     * @return mixed
     */
    private function getObject($arr)
    {
        $mapping = $this->getMapping();
        $instance = $this->getInstance();
        foreach ($mapping as $columnName) {
            $method = 'set' . ucfirst($this->getFieldCanonicalName($columnName));
            $instance->$method($arr[$columnName]);
        }
        return $instance;
    }

    /**
     * @param object $obj
     * @return array
     */
    private function getArray($obj)
    {
        $mapping = $this->getMapping();
        $list = array();
        foreach ($obj as $value) {
            $method = 'get' . ucfirst($this->getFieldCanonicalName($value));
            $list[$mapping[$value]] = $this->$method();
        }
        return $list;
    }

    protected $error;

    /**
     * @return null|\PDO
     */
    protected function getConnection()
    {
        $conf = \Config::GetInstance();
        $dsn = 'mysql:host=' . $conf->getHost() . ';dbname=' . $conf->getDbname();

        $options = array(
            \PDO::ATTR_PERSISTENT => true,
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
        );

        try {
            $dbh = new \PDO($dsn, $conf->getUser(), $conf->getPass(), $options);
            $dbh->exec("set names utf8");
        } catch (\PDOException $e) {
            $dbh = null;
            $this->error = $e->getMessage();
        }
        return $dbh;
    }

    /**
     * @return array
     */

    //выполнить кастомный запрос
    protected function exec($queryString)
    {
        $db = $this->getConnection();
        $checker = $db->exec($queryString);
        if ($checker == null){
        $stmt = $db->query($queryString);
        $stmt->setFetchMode(\PDO::FETCH_ASSOC);
        $result = $stmt->fetchAll();
        $list = array();
        foreach ($result as $val) {
            $list[] = $this->getObject($val);
        }
        return $list;}
        else return null;
    }

    public function findAll()
    {
        $db = $this->getConnection();
        $query = 'SELECT * FROM ' . $this->getTableName();
        $stmt = $db->query($query);
        $stmt->setFetchMode(\PDO::FETCH_ASSOC);
        $result = $stmt->fetchAll();
        $list = array();
        foreach ($result as $value) {
            $list[] = $this->getObject($value);
        }
        return $list;
    }

    /**
     * @param int $id
     * @return mixed
     */
    public function find($id)
    {
        $db = $this->getConnection();
        $query = 'SELECT * FROM ' . $this->getTableName() . ' WHERE id=' . $id;
        $stmt = $db->query($query);
        $stmt->setFetchMode(\PDO::FETCH_ASSOC);
        $result = array($stmt->fetch());
        return (count($result) > 0) ? $this->getObject($result[0]) : null;
    }

    /**
     * @param array $obj
     * @return string
     */
    public function add($obj)
    {
        $db = $this->getConnection();

        $obj = (is_array($obj)) ? $this : $this->getArray($obj);

        $keyList = implode(", ", array_keys($obj));
        $valList = array_values($obj);
        foreach ($valList as &$value) {
            $value = '"' . $value . '"';
        }
        $valList = implode(", ", $valList);
        $query = 'INSERT INTO ' . $this->getTableName() . ' (' . $keyList . ') VALUES (' . $valList . ');';
        $db->query($query);
        return $db->lastInsertId();
    }

    /**
     * @param array $obj
     */
    public function update($obj)
    {
        $db = $this->getConnection();

        $assignment = array();
        foreach ($obj as $key => $value) {
            $assignment[] = $key . '="' . $value . '"';
        }
        $assignment = implode(", ", $assignment);
        $query = 'UPDATE ' . $this->getTableName() . ' SET ' . $assignment . ' WHERE id=' . $obj["id"];
        $db->query($query);
    }

    /**
     * @param int $id
     */
    public function delete($id)
    {
        $db = $this->getConnection();
        try {
            $query = 'DELETE FROM ' . $this->getTableName() . ' WHERE id=' . $id;
            $db->exec($query);
        } catch (\PDOException $e) {
            $e->getMessage();
        }
    }

    /**
     * @param array $obj
     */
    public function save($obj)
    {
        if ($obj["id"] == 0 || !isset($obj["id"])) $this->add($obj);
        else $this->update($obj);
    }

    /**
     * @param array $criteria
     * @return array
     */
    public function findBy($criteria)
    {
        $db = $this->getConnection();

        $criteriaList = array();

        foreach ($criteria as $key => $value) {
            $criteriaList[] = $key . '="' . $value . '"';
        }
        $query = 'SELECT * FROM ' . $this->getTableName() . ' WHERE ' . implode(" AND ", $criteriaList);
        $stmt = $db->query($query);
        $stmt->setFetchMode(\PDO::FETCH_ASSOC);
        $result = $stmt->fetchAll();
        $list = array();
        foreach ($result as $value) {
            $list[] = $this->getObject($value);
        }
        return $list;
    }
}